/**
 * info: init
 * date: 2011-03-06
 * version: 1.0
 */
package com.pxingg.algorithm;

import com.pxingg.Constants;

/**
 * 这个类用来保存收到的消息信息，共别的类解析，分解成一个一个的消息<br>
 * 
 * @author pxingg
 * 
 */
public class ByteArrayQueue
{
    /**
     * 用了保存当前的字节数据
     */
    byte[] data;

    /**
     * 消息缓冲的总长度，表示最多能够缓冲多少字节
     */
    int capacity;

    /**
     * 指向消息头
     */
    int front;
    /**
     * 指向消息的的结尾
     */
    int rear;

    /**
     * 表示当前消息的字节数
     */
    int count;

    /**
     * 默认构造64K字节的缓冲区
     */
    public ByteArrayQueue()
    {
        capacity = Constants.DEFAULT_MESSAGE_BUFFER_LENGTH;
        data = new byte[capacity];
        front = 0;
        rear = capacity - 1;
        count = 0;
    }

    /**
     * 构造制定的容量大小
     * 
     * @param capacity
     *            所需要的缓冲区大小，单位字节
     */
    public ByteArrayQueue(final int capacity)
    {
        this.capacity = capacity;
        data = new byte[capacity];
        front = 0;
        rear = capacity - 1;
        count = 0;
    }

    /**
     * 在队列末尾增加一个自己
     * 
     * @param data
     *            需要保存到队列中的字节数据
     * @return true : 数据添加成功<br>
     *         false: 缓冲区已满，添加数据失败
     */
    public synchronized boolean append(final byte data)
    {
        if (count >= capacity)
        {
            return false;
        }

        count++;

        rear = ((rear + 1) == capacity) ? 0 : (rear + 1);
        this.data[rear] = data;
        return true;
    }

    /**
     * 在队列末尾增加一个字节数组
     * 
     * @param data
     *            需要保存到队列中的字节数组
     * @return true : 数据添加成功<br>
     *         false: 缓冲区已满，添加数据失败
     */
    public boolean append(final byte data[])
    {
        return append(data, data.length);
    }

    /**
     * 在队列末尾增加一个字节数组指定的前N个字节
     * 
     * @param data
     *            需要保存到队列中的字节数组
     * @param length
     *            需要保存到队列中的字节数组的长度
     * @return true : 数据添加成功<br>
     *         false: 缓冲区已满，添加数据失败
     */
    public synchronized boolean append(final byte data[], final int length)
    {
        if (data == null || length < 0)
        {
            // ignore this data
            return true;
        }
        final int minLength = data.length > length ? length : data.length;
        if (count + minLength > capacity)
        {
            return false;
        }

        count += minLength;

        for (int i = 0; i < minLength; i++)
        {
            rear = ((rear + 1) == capacity) ? 0 : (rear + 1);
            this.data[rear] = data[i];
        }
        return true;
    }

    /**
     * 清空队列中的所有数据
     */
    public void clear()
    {
        front = 0;
        rear = capacity - 1;
        count = 0;
    }

    /**
     * 返回当前能够容纳的字节数
     * 
     * @return 当前能够容纳的字节数
     */
    public int currentCapacity()
    {
        return capacity - count;
    }

    /**
     * 判断队列是否为空
     * 
     * @return true : 队列为空<br>
     *         false: 队列不为空
     */
    public boolean empty()
    {
        return count == 0;
    }

    /**
     * 判断队列是否已满
     * 
     * @return true : 队列已满<br>
     *         false: 队列不满
     */
    public boolean full()
    {
        return count == capacity;
    }

    /**
     * 返回当前能够容纳的字节数
     * 
     * @return 当前能够容纳的字节数
     */
    public int getCapacity()
    {
        return capacity;
    }

    /**
     * 获取队列中最开始的length个字节
     * 
     * @param length
     *            需要获取的数据的长度
     * @return 从队列中获取的数据<br>
     *         null 表示没有足够数据可以获取,或者length为0
     */
    public synchronized byte[] retrieve(final int length)
    {
        if (length > count)
        {
            // 队列中没有足够的数据，或者length=0
            return null;
        }

        final byte[] dataNeed = new byte[length];
        int dataIndex = front;

        for (int i = 0; i < length; i++)
        {
            dataNeed[i] = data[dataIndex];
            dataIndex = ((dataIndex + 1) == capacity) ? 0 : (dataIndex + 1);
        }

        return dataNeed;
    }

    /**
     * 从队列中移除最开始的length个字节
     * 
     * @param length
     *            需要移除的数据的长度
     * @return 从队列中移除的数据<br>
     *         null 表示没有足够的数据可以移除,或者length为0
     */
    public synchronized byte[] serve(final int length)
    {
        if (length > count)
        {
            // 队列中没有足够的数据
            return null;
        }

        final byte[] dataNeed = new byte[length];

        for (int i = 0; i < length; i++)
        {
            dataNeed[i] = data[front];
            front = ((front + 1) == capacity) ? 0 : (front + 1);
        }

        count -= length;

        return dataNeed;
    }

    /**
     * 把队列中b和b之前的数据移除
     * 
     * @param b
     *            获取数据截止的字节
     * @return 从队列中移除的数据<br>
     *         null 没有找到结束字符，表面目前所需要的数据还不完整
     */
    public synchronized byte[] serveUntill(final byte b)
    {
        if (empty())
        {
            return null;
        }

        int length = 0;
        int index = front;
        while (true)
        {
            // 长度增加1
            length++;
            if (data[index] == b)
            {
                break;
            }

            if (length == count)
            {
                // 遍历了队列中所有的数据，但是没有找到结束字符
                return null;
            }
            index = ((index + 1) == capacity) ? 0 : (index + 1);
        }

        // 返回找到的字符数组
        return serve(length);
    }

    /**
     * 获取当前已经保存的字节数
     * 
     * @return 当前已经保存的字节数
     */
    public int size()
    {
        return count;
    }

    /**
     * 试图获取队列中最开始的length个字节，如果没有length个字节，就获取全部数据
     * 
     * @param length
     *            需要获取的数据的长度
     * @return 从队列中获取的数据<br>
     *         null 表示没有足够的数据可以获取
     */
    public synchronized byte[] tryRetrieve(final int length)
    {
        final int minLength = count > length ? length : count;

        return retrieve(minLength);
    }

    /**
     * 试图从队列中移除最开始的length个字节，如果没有length个字节，就移除全部部数据
     * 
     * @param length
     *            需要移除的数据的长度
     * @return 从队列中移除的数据<br>
     *         null 表示没有足够的数据可以移除,或者length为0
     */
    public synchronized byte[] tryServe(final int length)
    {
        final int minLength = count > length ? length : count;

        return serve(minLength);
    }
}
